/*
 * ESPRESSIF MIT License
 *
 * Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
 *
 * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
 * it is free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/sens_reg.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_smartconfig.h"
#include "esp_system.h"
#include "board.h"
#include "periph_wifi.h"
#include "esp_peripherals.h"

static const char *TAG = "PERIPH_WIFI";

#define VALIDATE_WIFI(periph, ret) if (!(periph && esp_periph_get_id(periph) == PERIPH_ID_WIFI)) { \
    ESP_LOGE(TAG, "Invalid WIFI periph, at line %d", __LINE__);\
    return ret;\
}

#define DEFAULT_RECONNECT_TIMEOUT_MS (1000)

typedef struct periph_wifi *periph_wifi_handle_t;

struct periph_wifi {
    periph_wifi_state_t wifi_state;
    bool is_open;
    char *ssid;
    char *password;
    uint8_t max_connection;
    wifi_auth_mode_t authmode;
};

static esp_periph_handle_t g_periph = NULL;

// static esp_err_t wifi_send_cmd(esp_periph_handle_t periph, int cmd, int data)
// {
//     return esp_periph_send_event(periph, cmd, (void*)data, 0);
// }

periph_wifi_state_t periph_wifi_is_connected(esp_periph_handle_t periph)
{
    VALIDATE_WIFI(periph, false);
    periph_wifi_handle_t wifi = (periph_wifi_handle_t)esp_periph_get_data(periph);
    return wifi->wifi_state;
}

static esp_err_t _wifi_event_callback(void *ctx, system_event_t *event)
{
    esp_periph_handle_t self = (esp_periph_handle_t)ctx;
    periph_wifi_handle_t periph_wifi = (periph_wifi_handle_t)esp_periph_get_data(self);
    switch (event->event_id) {
        case SYSTEM_EVENT_AP_START:                     // ap 模式启动
            periph_wifi->wifi_state = PERIPH_WIFI_START;
            break;
        case SYSTEM_EVENT_AP_STOP:                      // ap 模式停止
            periph_wifi->wifi_state = PERIPH_WIFI_STOP;
            break;
        case SYSTEM_EVENT_AP_PROBEREQRECVED:            // 收到链接请求 
            break;
        case SYSTEM_EVENT_AP_STACONNECTED:              // sta 链接
            break;
        case SYSTEM_EVENT_AP_STADISCONNECTED:           // sta 断开开链接
            esp_periph_send_event(self, PERIPH_WIFI_STACONNECTED, NULL, 0);
            break;
        case SYSTEM_EVENT_AP_STAIPASSIGNED:             // sta 分配ip
            esp_periph_send_event(self, PERIPH_WIFI_STADISCONNECTED, NULL, 0);
            break;
        default:
            break;
    }
    return ESP_OK;
}

static esp_err_t _wifi_run(esp_periph_handle_t self, audio_event_iface_msg_t *msg)
{
    esp_periph_send_event(self, msg->cmd, NULL, 0);
    return ESP_OK;
}

static esp_err_t _wifi_init(esp_periph_handle_t self)
{
    periph_wifi_handle_t periph_wifi = (periph_wifi_handle_t)esp_periph_get_data(self);
    wifi_config_t wifi_config;

    if (periph_wifi->is_open) {
        ESP_LOGE(TAG, "Wifi has initialized");
        return ESP_FAIL;
    }

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    if (esp_event_loop_get_queue() == NULL) {
        ESP_ERROR_CHECK(esp_event_loop_init(_wifi_event_callback, self));
    } else {
        esp_event_loop_set_cb(_wifi_event_callback, self);
    }

    memset(&wifi_config, 0x00, sizeof(wifi_config_t));
    if (periph_wifi->ssid) {
        strcpy((char *)wifi_config.ap.ssid, periph_wifi->ssid);
        wifi_config.ap.ssid_len = strlen((char *)wifi_config.ap.ssid);
        ESP_LOGD(TAG, "WIFI_SSID=%s", wifi_config.ap.ssid);
        if (periph_wifi->password) {
            strcpy((char *)wifi_config.ap.password, periph_wifi->password);
            ESP_LOGD(TAG, "WIFI_PASS=%s", wifi_config.ap.password);
        }
        if(periph_wifi->max_connection) {
            wifi_config.ap.max_connection = periph_wifi->password;
        }
        if(periph_wifi->authmode) {
            wifi_config.ap.authmode = periph_wifi->authmode;
        }
        ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
        ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
        ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
    }
    ESP_ERROR_CHECK(esp_wifi_start());
    periph_wifi->is_open = true;
    periph_wifi->wifi_state = PERIPH_WIFI_UNCHANGE;
    return ESP_OK;
}

static esp_err_t _wifi_destroy(esp_periph_handle_t self)
{
    periph_wifi_handle_t periph_wifi = (periph_wifi_handle_t)esp_periph_get_data(self);
    esp_wifi_stop();
    esp_wifi_deinit();
    free(periph_wifi->ssid);
    free(periph_wifi->password);
    free(periph_wifi);
    g_periph = NULL;
    return ESP_OK;
}

esp_periph_handle_t periph_wifi_init(periph_wifi_cfg_t *config)
{
    esp_periph_handle_t periph = NULL;
    periph_wifi_handle_t periph_wifi = NULL;
    bool _success =
        (
            (periph = esp_periph_create(PERIPH_ID_WIFI, "periph_wifi")) &&
            (periph_wifi = calloc(1, sizeof(struct periph_wifi))) &&
            (config->ssid ? (bool)(periph_wifi->ssid = strdup(config->ssid)) : true) &&
            (config->password ? (bool)(periph_wifi->password = strdup(config->password)) : true) &&
            (config->max_connection ? (bool)(periph_wifi->max_connection = config->max_connection) : true) &&
            (config->authmode ? (bool)(periph_wifi->authmode = config->authmode) : true)
        );

    AUDIO_MEM_CHECK(TAG, _success, goto _periph_wifi_init_failed);

    esp_periph_set_data(periph, periph_wifi);
    esp_periph_set_function(periph, _wifi_init, _wifi_run, _wifi_destroy);
    g_periph = periph;
    return periph;

_periph_wifi_init_failed:
    if (periph_wifi) {
        free(periph_wifi->ssid);
        free(periph_wifi->password);
        free(periph_wifi);
    }
    return NULL;
}
